import type { AxiosRequestConfig, AxiosResponse } from "axios";

import { beforeEach, describe, expect, it, vi } from "vitest";

import { FileUploader } from "./uploader";

describe("fileUploader", () => {
  let fileUploader: FileUploader;
  // Mock the AxiosInstance
  const mockAxiosInstance = {
    post: vi.fn(),
  } as any;

  beforeEach(() => {
    fileUploader = new FileUploader(mockAxiosInstance);
  });

  it("should create an instance of FileUploader", () => {
    expect(fileUploader).toBeInstanceOf(FileUploader);
  });

  it("should upload a file and return the response", async () => {
    const url = "https://example.com/upload";
    const file = new File(["file content"], "test.txt", { type: "text/plain" });
    const mockResponse: AxiosResponse = {
      config: {} as any,
      data: { success: true },
      headers: {},
      status: 200,
      statusText: "OK",
    };

    (
      mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
    ).mockResolvedValueOnce(mockResponse);

    const result = await fileUploader.upload(url, { file });
    expect(result).toEqual(mockResponse);
    expect(mockAxiosInstance.post).toHaveBeenCalledWith(
      url,
      expect.any(FormData),
      {
        headers: {
          "Content-Type": "multipart/form-data",
        },
      },
    );
  });

  it("should merge provided config with default config", async () => {
    const url = "https://example.com/upload";
    const file = new File(["file content"], "test.txt", { type: "text/plain" });
    const mockResponse: AxiosResponse = {
      config: {} as any,
      data: { success: true },
      headers: {},
      status: 200,
      statusText: "OK",
    };

    (
      mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
    ).mockResolvedValueOnce(mockResponse);

    const customConfig: AxiosRequestConfig = {
      headers: { "Custom-Header": "value" },
    };

    const result = await fileUploader.upload(url, { file }, customConfig);
    expect(result).toEqual(mockResponse);
    expect(mockAxiosInstance.post).toHaveBeenCalledWith(
      url,
      expect.any(FormData),
      {
        headers: {
          "Content-Type": "multipart/form-data",
          "Custom-Header": "value",
        },
      },
    );
  });

  it("should handle errors gracefully", async () => {
    const url = "https://example.com/upload";
    const file = new File(["file content"], "test.txt", { type: "text/plain" });
    (
      mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
    ).mockRejectedValueOnce(new Error("Network Error"));

    await expect(fileUploader.upload(url, { file })).rejects.toThrow(
      "Network Error",
    );
  });

  it("should handle empty URL gracefully", async () => {
    const url = "";
    const file = new File(["file content"], "test.txt", { type: "text/plain" });
    (
      mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
    ).mockRejectedValueOnce(new Error("Request failed with status code 404"));

    await expect(fileUploader.upload(url, { file })).rejects.toThrow(
      "Request failed with status code 404",
    );
  });

  it("should handle null URL gracefully", async () => {
    const url = null as unknown as string;
    const file = new File(["file content"], "test.txt", { type: "text/plain" });
    (
      mockAxiosInstance.post as unknown as ReturnType<typeof vi.fn>
    ).mockRejectedValueOnce(new Error("Request failed with status code 404"));

    await expect(fileUploader.upload(url, { file })).rejects.toThrow(
      "Request failed with status code 404",
    );
  });
});
